大家好,我是 Eric。
昨天我們介紹用過勾點 (hook) 的機制,製作 Contact Form 7 的擴充功能。今天我們將進一步介紹透過 WordPress 內建的方法,製作 AJAX 的無限捲動 (infinite scroll) 功能。
有關 WordPress AJAX 的作法,除了這篇文章外,也可以參考社群夥伴阿峻的〈開發 AJAX 功能的正確流程〉一文。
要透過 WordPress 製作 AJAX 端點的方法有 wp_ajax_{動作函式}
跟 wp_ajax_nopriv_{動作函式}
兩種,作法如下:
/* 讓已登入使用可以存取 AJAX */
add_action( 'wp_ajax_eric_load_more', 'eric_load_more' );
/* 讓未登入使用可以存取 AJAX */
add_action( 'wp_ajax_nopriv_eric_load_more', 'eric_load_more' );
function eric_load_more() {
/* 回傳錯誤 */
wp_send_json_error( array(
'code' => 500, // 錯誤代碼
'data' => '',
'msg' => '',
)
);
/* 回傳正確資料 */
wp_send_json_success( array(
'code' => 200,
'data' => '{{回傳資料}}',
)
);
}
在開始製作 AJAX 端點之前,因為我們會將查詢後的結果附加在現有文章列表下,因此了解文章列表的結構,對於後續拼裝 HTML 很重要。我們一樣以 Twenty Twenty 為例,我們可以從 template-parts/content.php 與 template-parts/entry-header.php 找到它的架構。
add_action( 'wp_ajax_eric_load_more', 'eric_load_more' );
add_action( 'wp_ajax_nopriv_eric_load_more', 'eric_load_more' );
function eric_load_more() {
$nonce = $_POST['nonce'];
$max_num_pages = $_POST['max_num_pages'];
$current_page = $_POST['current_page'];
$found_posts = $_POST['found_posts'];
$post_type = $_POST['post_type']; //可以擴展自訂內容類型
$category = $_POST['category']; //用於文章時可以支援同分類文章查詢
/* 如果無法通過 nonce 驗證,則顯示系統錯誤 */
if ( ! wp_verify_nonce ( $nonce, 'ajax-nonce' ) ) {
wp_send_json_error( array(
'code' => 500, // 錯誤代碼
'data' => '',
'msg' => 'Nonce 驗證失敗',
)
);
}
/* 如果沒有回傳必要的參數,則顯示錯誤 */
if ( ! isset( $max_num_pages ) || $max_num_pages == '' ||
! isset( $current_page ) || $current_page == '' ||
! isset( $found_posts ) || $found_posts == '' ||
! isset( $post_type ) || $post_type == '' ) {
wp_send_json_error( array(
'code' => 500, // 錯誤代碼
'data' => '',
'msg' => '未回傳必要的參數',
)
);
}
/* 開始製作自訂迴圈 */
$args = array(
'post_type' => $post_type,
'paged' => intval( $current_page ) + 1,
'category_name' => $category,
'post_status' => 'publish',
);
$the_query = new WP_Query( $args );
$output = '';
while( $the_query -> have_posts() ){
$output .= '<hr class="post-separator styled-separator is-style-wide section-inner" aria-hidden="true" />'; //因為 Twenty Twenty 會加入分隔線,所以先接上
$the_query -> the_post();
/* 參考 template-parts/content.php */
$output .= '<article class="' . implode( ' ', get_post_class() ) . '" id="post-' . get_the_ID() . '">';
/* 參考 template-parts/header.php */
$output .= '<header class="entry-header has-text-align-center">';
$output .= '<div class="entry-header-inner section-inner medium">';
$show_categories = apply_filters( 'twentytwenty_show_categories_in_entry_header', true );
if ( true === $show_categories && has_category() ) {
$output .= '<div class="entry-categories">';
$output .= '<span class="screen-reader-text">' . __( 'Categories', 'twentytwenty' ) . '</span>';
$output .= '<div class="entry-categories-inner">';
$output .= get_the_category_list( ' ' );
$output .= '</div><!-- .entry-categories-inner -->';
$output .= '</div><!-- .entry-categories -->';
}
$output .= '<h2 class="entry-title heading-size-1"><a href="' . esc_url( get_permalink() ) . '">' . get_the_title() . '</a></h2>';
$output .= twentytwenty_get_post_meta( get_the_ID(), 'single-top' );
$output .= '</div><!-- .entry-header-inner -->';
$output .= '</header>';
$is_thin = is_page_template( 'templates/template-full-width.php' ) ? '' : 'thin';
$output .= '<div class="post-inner ' . $is_thin . '">';
$output .= '<div class="entry-content">';
$output .= get_the_content( __( 'Continue reading', 'twentytwenty' ) );
$output .= '</div><!-- .entry-content -->';
$output .= '</div><!-- .post-inner -->';
$output .= '<div class="section-inner">';
$output .= twentytwenty_get_post_meta( get_the_ID(), 'single-bottom' );
$output .= '</div><!-- .section-inner -->';
$output .= '</article><!-- .post -->';
}
wp_reset_postdata();
wp_send_json_success( array(
'code' => 200,
'data' => $output
)
);
}
在了解 AJAX 的勾點後,我們要透過 jQuery 來存取這個 AJAX 端點。首先,我們先在子佈景主題中,建立 assets/js
的資料夾,並在其中新增 infinite-scroll.js 這個檔案。接著在子佈景主題的 functions.php 或 Code Snippets 中,插入以下程式碼,引用 infinite-scroll.js 這個檔案:
add_action( 'wp_enqueue_scripts', 'eric_load_more_scripts' );
function eric_load_more_scripts() {
wp_enqueue_script( 'load-more', get_stylesheet_directory_uri() . '/assets/js/infinite-scroll.js', array( 'jquery' ) );
/* 用 wp_localize_script 將 JavaScript 檔案中需要用到的物件傳送過去 */
/* wp_localize_script( '處理常式 handler 名稱', 'JavaScript 物件名稱', array(
* '物件屬性' => '屬性值',
* '物件屬性2' => '屬性值',
* ) );
*/
wp_localize_script( 'load-more', 'inf', array(
/* WordPress AJAX 的統一端點是 admin-ajax.php */
'ajaxurl' => admin_url( 'admin-ajax.php' );
'nonce' => wp_create_nonce( 'ajax-nonce' ); //這個 nonce 的名稱要與 eric_load_more() 中一致
'msg' => array(
'query_end' => '查詢完畢',
);
) );
}
假設我們要透過連結來自訂「繼續載入」的按鈕,我們可以在子佈景主題中的 index.php 中加入:
if ( is_home() ) { ?>
<a href="#" class="button load-more">繼續載入</a>
<?php }
接著,我們在 infinite-scroll.js 這個檔案中加入以下的 JavaScript 程式碼:
(function($){
$( document ).ready( function(){
$( 'a.load-more' ).click( function(e){
e.preventDefault();
$( this ).attr( 'disabled' , true);
var that = $( this );
if ( inf.posts.max_num_pages == inf.posts.current_page ) {
$( this ).text( inf.msg.query_end );
$( this ).unbind( 'click' );
return;
}
var data = {
'action' : 'eric_load_more', //記得檢查 action 有沒有下對
'nonce' : inf.nonce,
'max_num_pages': inf.posts.max_num_pages,
'current_page' : inf.posts.current_page,
'found_posts' : inf.posts.found_posts,
'post_type' : inf.posts.post_type,
'category' : inf.posts.category,
};
$.post( inf.ajaxurl, data, function( res ) {
if ( res.success ) {
$( '#site-content' ).append( res.data.data );
inf.posts.current_page += 1;
that.attr('disabled', false);
} else {
alert('Oops! Sorry error occurred!');
}
}).fail(function (xhr, status, error) {
alert('Oops! Sorry error occurred! Internet issue.');
});
});
});
}(jQuery));
看到這裡,可能會注意到 inf
這個物件下還有 posts
這個屬性,因此我們需要再額外定義這個屬性。我們再回到 index.php,加入以下程式碼:
/* index.php Line 20*/
<main id="site-content" role="main">
<?php if ( is_home() || is_category() : ?>
<script>
if ( typeof(inf) === undefined ){
var inf = {};
}
<?php
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$postsparam = array(
'post_type' => get_post_type(),
'category' => $wp_query -> get_queried_object() -> slug,
'max_num_pages' => $wp_query -> max_num_pages,
'found_posts' => $wp_query -> found_posts,
'current_page' => $paged,
);
?>
inf.posts = <?php echo json_encode( $postsparam ) ?>;
</script>
<?php endif; ?>
<?php
今天介紹了如何透過 AJAX 在 Twenty Twenty 的子佈景主題文章列表頁中,加入了無限捲動的功能。
明天,我們會再介紹如何將自己的佈景主題加上多語系支援,讓佈景主題或外掛的字串可以供其他人翻譯。